package wsi.function;

import java.io.File;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.rygh.core.util.UIo;
import org.rygh.core.xml.UXml;
import org.rygh.core.xml.XmlLeaf;
import org.rygh.core.xml.XmlObject;
import org.rygh.core.xml.XmlParser;
import org.rygh.core.xml.XmlTree;
import org.rygh.semantic.gui.swing.input.Input;
import org.rygh.semantic.model.rdf.Entity;

import wsi.dataguide.DataGuide;
import wsi.dataguide.DataGuideLoader;
import wsi.input.InputSelector;
import wsi.script.AppConfig;

public class DirManager {
	public final static String DATA_GUIDE_WITH_VALUES = "_debug_dg_with_values.xml";
	public final static String DATA_GUIDE_SCHEMA = "dataguide_schema.xml";

	private final Function function;

	private final File functionDir;
	private final File debugDir;
	private final File contextGraphDir;
	private final File xmlDir;
	private final File xmlAppendDir;

	private Input[] inputs;
	private DataGuide dataGuide;

	public DirManager(Function function) {
		this.function = function;

		File fdir = AppConfig.shared().getFunctionsFolder();
		File serviceDir = new File(fdir, function.getService());

		functionDir = new File(serviceDir, function.getName());
		contextGraphDir = new File(functionDir, "context_graph");
		debugDir = new File(functionDir, "debug");
		xmlDir = new File(functionDir, "xmls");
		xmlAppendDir = new File(xmlDir, "appended");

		if (!functionDir.exists())
			functionDir.mkdir();

		if (!debugDir.exists())
			debugDir.mkdir();

		if (!contextGraphDir.exists())
			contextGraphDir.mkdir();

		if (!xmlDir.exists())
			xmlDir.mkdir();

		if (!xmlAppendDir.exists())
			xmlAppendDir.mkdir();
	}

	public File getContextGraphDir() {
		return contextGraphDir;
	}

	public DataGuide getDataGuide() {
		return dataGuide;
	}

	public String getDataGuideSchema() {
		File file = new File(functionDir, DATA_GUIDE_SCHEMA);
		if (!file.exists())
			return null;

		String xml = UIo.readFile(file);

		return xml;
	}

	public File getDebugDir() {
		return debugDir;
	}

	public Map<Input, File> getFileMap() {
		RuntimeInfo runtime = function.getRuntimeInfo();

		File[] files = null;

		if (runtime != null && runtime.isAssistNeeded()
				&& runtime.isAssistReady())
			files = getXmlAppendFiles();
		else
			files = getXmlFiles();

		return getFileMap(files);
	}

	private Map<Input, File> getFileMap(File[] files) {
		Map<Input, File> map = new HashMap<>();

		for (Input input : inputs) {
			File file = null;
			String fname = input.toFilename();

			for (File f : files) {
				if (fname.equals(UIo.getFileNameWithoutExtension(f.getName()))) {
					file = f;
					break;
				}
			}

			if (file == null) {
				System.out.printf("[warn] no xml file for input %s (%s)\n",
						input, fname);
				continue;
			}

			map.put(input, file);
		}

		return map;
	}

	public Function getFunction() {
		return function;
	}

	public File getFunctionDir() {
		return functionDir;
	}

	public Input[] getInputs() {
		return inputs;
	}

	public String getServiceName() {
		return function.getService();
	}

	public File getXmlAppendDir() {
		return xmlAppendDir;
	}

	public File[] getXmlAppendFiles() {
		return xmlAppendDir.listFiles(UXml.XML_FILTER);
	}

	public File getXmlDir() {
		return xmlDir;
	}

	public File[] getXmlFiles() {
		return xmlDir.listFiles(UXml.XML_FILTER);
	}

	public boolean loadDataGuide() {
		if (!functionDir.exists())
			return false;

		// should load values here, to compute functionality.
		File file = new File(debugDir, DATA_GUIDE_WITH_VALUES);
		if (!file.exists())
			return false;

		String xml = UIo.readFile(file);

		DataGuideLoader loader = new DataGuideLoader(xml);
		dataGuide = loader.load();

		if (!loader.isValid())
			return false;

		if (dataGuide.getConcreteRoot() == null) {
			System.out.println("[warn] empty dataguide tree.");
			return false;
		}

		// System.out.println("dataGuide " + dataGuide);
		// UIo.saveFile(new File("e:/a.xml"), dataGuide.toString(0));

		return true;
	}

	/**
	 * this method loads inputs from input file only. it will not try to use any
	 * input selector to obtain input values.
	 */
	public void loadInputs() {
		setInputs(null);

		File file = new File(functionDir, InputSelector.CANDIDATE_INPUTS_XML);

		if (!file.exists()) {
			System.out.println("[warn] input file does not exist.");
			return;
		}

		String content = UIo.readFile(file);
		XmlParser parser = new XmlParser(content);
		XmlTree xmltree = parser.parse();

		if (!parser.isValid()) {
			System.out.println("[warn] input file is not valid.");
			return;
		}

		List<XmlLeaf> nodes = xmltree.getNodesByXPath("/inputs/input");

		Entity entity;
		Input input;

		List<Input> list = new LinkedList<>();
		int c = Math.min(nodes.size(), AppConfig.shared().getInputSize());

		for (int i = 0; i < c; i++) {
			XmlLeaf node = nodes.get(i);
			XmlObject obj = node.getValue();

			entity = new Entity(obj.getAttribute("entity"), false);
			input = new Input(entity, null, obj.getValue());

			list.add(input);
		}

		setInputs(list.toArray(new Input[list.size()]));
	}

	public void saveToFunctionDir(String filename, String content) {
		UIo.saveFile(new File(getFunctionDir(), filename), content);
	}

	public void setInputs(Input[] inputs) {
		this.inputs = inputs;
	}

}
